src/pages/now/[slug].astro 3.5 K raw
1
---
2
import PageLayout from "@/layouts/Base.astro";
3
import Weather from "@/components/now/Weather.astro";
4
import { POSTS_API } from "@/data/constants";
5
import { createMarkdownRenderer } from "@/utils";
6
7
export const prerender = false;
8
9
const md = await createMarkdownRenderer();
10
11
const { slug } = Astro.params;
12
13
if (!slug) {
14
	return Astro.redirect("/now");
15
}
16
17
interface PostDetail {
18
	short_id: string;
19
	title: string | null;
20
	slug: string;
21
	alias: string | null;
22
	canonical_url: string | null;
23
	published_date: string | null;
24
	meta_description: string | null;
25
	meta_image: string | null;
26
	lang: string;
27
	tags: string | null;
28
	content: string;
29
	created_at: string;
30
	updated_at: string;
31
	weather: string | null;
32
}
33
34
let title: string | null = "Post";
35
let description = "A post";
36
let contentHTML = "";
37
let publishedAt = "";
38
let errorMessage = "";
39
let weatherRaw = "";
40
let isError = false;
41
42
try {
43
	const res = await fetch(`${POSTS_API}/posts/${encodeURIComponent(slug)}`);
44
45
	if (res.status === 404) {
46
		Astro.response.status = 404;
47
		isError = true;
48
		title = "Not Found";
49
		errorMessage = "The post you're looking for doesn't exist.";
50
	} else if (!res.ok) {
51
		throw new Error(`HTTP ${res.status}`);
52
	} else {
53
		const post = (await res.json()) as PostDetail;
54
		title = post.title;
55
		description =
56
			post.meta_description ||
57
			(post.content ? post.content.slice(0, 160) : description);
58
		publishedAt = post.published_date
59
			? new Date(post.published_date).toLocaleDateString()
60
			: "";
61
		contentHTML = md.render(post.content || "");
62
		weatherRaw = post.weather || "";
63
	}
64
} catch (err) {
65
	console.error("Error fetching post:", err);
66
	Astro.response.status = 500;
67
	isError = true;
68
	title = "Error";
69
	errorMessage = "Something went wrong while loading this post.";
70
}
71
72
const meta = {
73
	title: title || "Post",
74
	description,
75
};
76
---
77
78
<PageLayout meta={meta}>
79
	<article>
80
		{isError ? (
81
			<>
82
				<h1 class="title mb-4">{title}</h1>
83
				<p class="text-zinc-400 mb-8">{errorMessage}</p>
84
				<a class="style-link" href="/now">&larr; Back to Now</a>
85
			</>
86
		) : (
87
			<>
88
				{title && <h1 class="title mb-2">{title}</h1>}
89
				<div class="flex text-sm text-zinc-400 items-center gap-x-3 gap-y-1 flex-wrap">
90
					<time>{publishedAt}</time>
91
					{weatherRaw && <Weather leadingSeparator weather={weatherRaw} />}
92
				</div>
93
				<div class="prose prose-invert max-w-none my-4">
94
					<Fragment set:html={contentHTML} />
95
				</div>
96
				<div class="mt-12 flex items-center justify-between">
97
					<a class="style-link" href="/now">&larr; Now</a>
98
          <div class="flex gap-6">
99
            <button
100
              id="share-btn"
101
              class="text-sm text-zinc-500 hover:text-zinc-700 transition-colors"
102
            >
103
              Share
104
            </button>
105
              <a 
106
                class="text-sm text-zinc-500 hover:text-zinc-700 transition-colors"
107
                href={ `mailto:contact@stevedylan.dev?subject=${'Re: ' + title}` }
108
                >
109
                Reply via Email
110
              </a>
111
          </div>
112
				</div>
113
			</>
114
		)}
115
	</article>
116
</PageLayout>
117
118
<script>
119
	const btn = document.getElementById("share-btn");
120
	if (btn) {
121
		btn.addEventListener("click", () => {
122
			const url = window.location.href;
123
			navigator.clipboard
124
				.writeText(url)
125
				.then(() => {
126
					const originalText = btn.textContent;
127
					btn.textContent = "Copied!";
128
					setTimeout(() => {
129
						btn.textContent = originalText;
130
					}, 2000);
131
				})
132
				.catch((err) => {
133
					console.error("Failed to copy URL:", err);
134
					alert("Failed to copy URL");
135
				});
136
		});
137
	}
138
</script>